home *** CD-ROM | disk | FTP | other *** search
- // VirtualDub - Video processing and capture application
- // Copyright (C) 1998-2001 Avery Lee
- //
- // This program is free software; you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation; either version 2 of the License, or
- // (at your option) any later version.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with this program; if not, write to the Free Software
- // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- #include <string.h>
- #include <stdio.h>
- #include <crtdbg.h>
-
- #include <windows.h>
- #include <mmsystem.h>
-
- #include "AVIAudioOutput.h"
-
- AVIAudioOutputBuffer::AVIAudioOutputBuffer(long bsize) {
- memset(&hdr, 0, sizeof hdr);
- hdr.lpData = new char[bsize];
- hdr.dwBufferLength = bsize;
- next = prev = NULL;
- dwBytesInBuffer = 0;
-
- }
-
- AVIAudioOutputBuffer::~AVIAudioOutputBuffer() {
- delete hdr.lpData;
- }
-
- BOOL AVIAudioOutputBuffer::init(HWAVEOUT hWaveOut) {
- return MMSYSERR_NOERROR == waveOutPrepareHeader(hWaveOut,&hdr,sizeof(WAVEHDR));
- }
-
- BOOL AVIAudioOutputBuffer::post(HWAVEOUT hWaveOut) {
- return MMSYSERR_NOERROR == waveOutWrite(hWaveOut,&hdr,sizeof(WAVEHDR));
- }
-
- void AVIAudioOutputBuffer::deinit(HWAVEOUT hWaveOut) {
- waveOutUnprepareHeader(hWaveOut, &hdr, sizeof(WAVEHDR));
- }
-
- AVIAudioOutput::AVIAudioOutput(long bufsize, int maxbufs) {
- this->maxbufs = maxbufs;
- this->bufsize = bufsize;
-
- numbufs = 0;
- pending = pending_tail = active = NULL;
- curState = STATE_NONE;
-
- fill_byte = (char)0x00;
- iBuffersActive = 0;
-
- hEventBuffersFree = CreateEvent(NULL,FALSE,FALSE,NULL);
- }
-
- AVIAudioOutput::~AVIAudioOutput() {
- AVIAudioOutputBuffer *nb;
-
- if (curState == STATE_SILENT) return;
-
- stop();
-
- while(nb = pending) {
- pending = pending->next;
- nb->deinit(hWaveOut);
- delete nb;
- }
-
- while(nb = active) {
- active = active->next;
- nb->deinit(hWaveOut);
- delete nb;
- }
-
- if (curState >= STATE_OPENED)
- waveOutClose(hWaveOut);
-
- if (hEventBuffersFree)
- CloseHandle(hEventBuffersFree);
- }
-
- BOOL AVIAudioOutput::init(WAVEFORMATEX *wf) {
- MMRESULT res;
- AVIAudioOutputBuffer *aaob;
-
- if (wf->wFormatTag == WAVE_FORMAT_PCM)
- if (wf->wBitsPerSample == 8)
- fill_byte = (char)0x80;
-
- if (MMSYSERR_NOERROR != (res = waveOutOpen(&hWaveOut, WAVE_MAPPER, wf, (DWORD)hEventBuffersFree, 0, CALLBACK_EVENT)))
- return FALSE;
-
- curState = STATE_OPENED;
- nSamplesPerSec = wf->nSamplesPerSec;
- nAvgBytesPerSec = wf->nAvgBytesPerSec;
-
- // Hmmm... we can't allocate buffers while the wave device
- // is active...
-
- while(numbufs < maxbufs) {
- aaob = new AVIAudioOutputBuffer(bufsize);
- if (!aaob) break;
-
- if (!aaob->init(hWaveOut)) {
- _RPT0(0,"Init failed\n");
- delete aaob;
- return FALSE;
- }
-
- #if 0
- {
- char buf[128];
- wsprintf(buf, "Allocated audio buffer of %ld bytes\n", aaob->hdr.dwBufferLength);
- OutputDebugString(buf);
- }
- #endif
-
- aaob->prev = NULL;
- aaob->next = pending;
- if (pending) pending->prev = aaob;
- else pending_tail = aaob;
- pending = aaob;
-
- ++numbufs;
- }
-
- lAvailSpace = numbufs * bufsize;
-
- waveOutPause(hWaveOut);
-
- return TRUE;
- }
-
- void AVIAudioOutput::go_silent() {
- curState = STATE_SILENT;
- }
-
- BOOL AVIAudioOutput::isSilent() {
- return curState == STATE_SILENT;
- }
-
- BOOL AVIAudioOutput::start() {
- if (curState == STATE_SILENT) return TRUE;
-
- if (curState < STATE_OPENED) return FALSE;
-
- if (MMSYSERR_NOERROR != waveOutRestart(hWaveOut))
- return FALSE;
-
- curState = STATE_PLAYING;
-
- return TRUE;
- }
-
- BOOL AVIAudioOutput::stop() {
- if (curState == STATE_SILENT) return TRUE;
-
- if (curState >= STATE_OPENED) {
- if (MMSYSERR_NOERROR != waveOutReset(hWaveOut))
- return FALSE;
-
- curState = STATE_OPENED;
- }
-
- return TRUE;
- }
-
- BOOL AVIAudioOutput::checkBuffers() {
- AVIAudioOutputBuffer *aaob,*aaob2;
- int found = 0;
-
- if (curState == STATE_SILENT) return TRUE;
-
- aaob = active;
- while(aaob) {
- if (aaob->hdr.dwFlags & WHDR_DONE) {
- aaob2=aaob->next;
- if (aaob->prev) aaob->prev->next = aaob->next; else active=aaob->next;
- if (aaob->next) aaob->next->prev = aaob->prev;
- aaob->hdr.dwFlags &= ~WHDR_DONE;
- aaob->dwBytesInBuffer = 0;
-
- aaob->prev = NULL;
- aaob->next = pending;
- if (pending) pending->prev = aaob;
- else pending_tail = aaob;
- pending = aaob;
-
- --iBuffersActive;
- lAvailSpace += aaob->hdr.dwBufferLength;
-
- aaob=aaob2;
- ++found;
- } else
- aaob=aaob->next;
- }
-
- // _RPT1(0,"%d buffers returned\n",found);
-
- return found>0;
- }
-
- BOOL AVIAudioOutput::waitBuffers(DWORD timeout) {
- if (curState == STATE_SILENT) return TRUE;
-
- if (hEventBuffersFree && timeout) {
- for(;;) {
- if (WAIT_OBJECT_0 != WaitForSingleObject(hEventBuffersFree, timeout))
- return FALSE;
-
- if (checkBuffers())
- return TRUE;
- }
- }
-
- return checkBuffers();
- }
-
- long AVIAudioOutput::avail() {
- checkBuffers();
- return lAvailSpace;
- }
-
- BOOL AVIAudioOutput::write(void *data, long len, DWORD timeout) {
- AVIAudioOutputBuffer *aaob;
- long tc;
-
- if (curState == STATE_SILENT) return TRUE;
-
- checkBuffers();
- // _RPT1(0,"writing %ld bytes\n",len);
-
- while(len) {
- if (!pending) {
-
- if (!waitBuffers(0) && timeout) {
- if (!waitBuffers(timeout)) {
- return FALSE;
- }
- continue;
- }
- break;
- }
-
- aaob = pending_tail;
-
- tc = aaob->hdr.dwBufferLength - aaob->dwBytesInBuffer;
- if (tc > len) tc=len;
-
- if (tc)
- memcpy((char *)aaob->hdr.lpData + aaob->dwBytesInBuffer, data, tc);
-
- lAvailSpace -= tc;
-
- // if the buffer is full, ship it out
-
- if ((aaob->dwBytesInBuffer += tc) >= aaob->hdr.dwBufferLength) {
- #if 0
- {
- char buf[128];
- wsprintf(buf, "Posting audio buffer of %ld/%ld bytes (%ld already active)\n", aaob->dwBytesInBuffer, aaob->hdr.dwBufferLength, iBuffersActive);
- OutputDebugString(buf);
- }
- #endif
- if (!postBuffer(aaob)) return FALSE;
- }
-
- len -= tc;
- data = (char *)data + tc;
- }
-
- return TRUE;
- }
-
- void AVIAudioOutput::flush() {
- if (pending_tail && pending_tail->dwBytesInBuffer)
- postBuffer(pending_tail);
- }
-
- BOOL AVIAudioOutput::finalize(DWORD timeout) {
- if (curState == STATE_SILENT) return TRUE;
-
- _RPT0(0,"AVIAudioOutput: finalizing output\n");
-
- flush();
-
- while(checkBuffers(), active)
- if (hEventBuffersFree != INVALID_HANDLE_VALUE)
- if (WAIT_OBJECT_0 != WaitForSingleObject(hEventBuffersFree, timeout))
- return FALSE;
-
- _RPT0(0,"AVIAudioOutput: finalized.\n");
-
- return TRUE;
- }
-
- BOOL AVIAudioOutput::postBuffer(AVIAudioOutputBuffer *aaob) {
- // delink from pending list
-
- if (aaob->prev) aaob->prev->next = aaob->next; else pending=aaob->next;
- if (aaob->next) aaob->next->prev = aaob->prev; else pending_tail=aaob->prev;
-
- // clear rest of data
-
- if (aaob->dwBytesInBuffer < aaob->hdr.dwBufferLength)
- memset((char *)aaob->hdr.lpData + aaob->dwBytesInBuffer, fill_byte, aaob->hdr.dwBufferLength-aaob->dwBytesInBuffer);
-
- // post to wave device
-
- aaob->hdr.dwFlags &= ~WHDR_DONE;
-
- if (!aaob->post(hWaveOut)) {
- _RPT0(0,"post failed!\n");
- return FALSE;
- }
-
- // link to active list
-
- aaob->prev = NULL;
- aaob->next = active;
- if (active) active->prev = aaob;
- active = aaob;
-
- ++iBuffersActive;
-
- return TRUE;
- }
-
- long AVIAudioOutput::position() {
- MMTIME mmtime;
-
- if (curState != STATE_PLAYING) return -1;
-
- mmtime.wType = TIME_SAMPLES;
-
- if (MMSYSERR_NOERROR != waveOutGetPosition(hWaveOut, &mmtime, sizeof mmtime))
- return -1;
-
- switch(mmtime.wType) {
- case TIME_BYTES:
- return MulDiv(mmtime.u.cb, 1000, nAvgBytesPerSec);
- case TIME_MS:
- return mmtime.u.ms;
- case TIME_SAMPLES:
- return MulDiv(mmtime.u.sample, 1000, nSamplesPerSec);
- }
-
- return -1;
- }
-
- bool AVIAudioOutput::isFrozen() {
- AVIAudioOutputBuffer *aaob;
-
- if (curState != STATE_PLAYING) return true;
-
- aaob = active;
- while(aaob) {
- if (!(aaob->hdr.dwFlags & WHDR_DONE))
- return false;
-
- aaob=aaob->next;
- }
-
- return true;
- }
-